# encoding: utf-8
# @File  : auth.py
# @Author: shaoyun
# @Date  :  2025/05/11
import secrets
import uuid
from datetime import datetime, timedelta
from typing import Dict, Any, Optional

from fastapi import APIRouter, Depends, HTTPException, Body
from fastapi.security import OAuth2PasswordRequestForm
from sqlalchemy import and_
from sqlalchemy.orm import Session

from app.api.dependencies import create_access_token, get_current_user, get_optional_current_user
from app.core.config import settings
from app.core.response import get_response
from app.core.security import verify_password
from app.core.status_codes import (
    SUCCESS,
    USER_Banned,
    USERNAME_NOT_EXIST,
    INCORRECT_PASSWORD,
    INACTIVE_USER,
    INVALID_TOKEN
)
from app.db.mysql import get_db
from app.models.refresh_token import RefreshToken
from app.models.user import User

router = APIRouter()


def create_refresh_token(user_id: str, db: Session) -> str:
    """创建新的 refresh token"""
    # 生成唯一的 refresh token
    token = secrets.token_urlsafe(64)
    token_id = str(uuid.uuid4())
    expires_at = datetime.utcnow() + timedelta(days=settings.REFRESH_TOKEN_EXPIRE_DAYS)

    # 保存到数据库
    db_token = RefreshToken(
        id=token_id,
        token=token,
        user_id=user_id,
        expires_at=expires_at,
        created_at=datetime.utcnow()
    )
    db.add(db_token)
    db.commit()

    return token


def revoke_user_tokens(user_id: str, db: Session, except_token: Optional[str] = None):
    """撤销用户的所有 refresh tokens（可选保留一个）"""
    query = db.query(RefreshToken).filter(
        RefreshToken.user_id == user_id,
        RefreshToken.is_revoked == False
    )

    if except_token:
        query = query.filter(RefreshToken.token != except_token)

    query.update({"is_revoked": True})
    db.commit()


@router.post("/token", response_model=Dict[str, Any])
async def login_for_access_token(
        form_data: OAuth2PasswordRequestForm = Depends(),
        db: Session = Depends(get_db)
):
    """
    用户登录接口
    - 验证用户名密码
    - 返回 access_token 和 refresh_token
    """
    # 查找用户
    user = db.query(User).filter(User.username == form_data.username).first()

    # 验证用户是否存在
    if not user:
        return get_response(USERNAME_NOT_EXIST)

    # 验证密码
    if not verify_password(form_data.password, str(user.hashed_password)):
        return get_response(INCORRECT_PASSWORD)

    # 验证用户是否激活
    if not user.is_active:
        return get_response(INACTIVE_USER)

    # 撤销该用户之前的所有 refresh tokens（可选）
    # 这样可以确保同一时间只有一个有效的登录会话
    revoke_user_tokens(str(user.id), db)

    # 创建 access token (短期 - 15分钟)
    access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
    access_token = create_access_token(
        subject=str(user.id),
        expires_delta=access_token_expires
    )

    # 创建 refresh token (长期 - 7天)
    refresh_token = create_refresh_token(str(user.id), db)

    return get_response(SUCCESS, data={
        "access_token": access_token,
        "refresh_token": refresh_token,
        "token_type": "bearer",
        "expires_in": settings.ACCESS_TOKEN_EXPIRE_MINUTES * 60,  # 秒
        "user": {
            "id": str(user.id),
            "username": user.username,
            "email": user.email,
            "role": user.role.value,
            "is_active": user.is_active
        }
    })


@router.post("/refresh", response_model=Dict[str, Any])
async def refresh_access_token(
        refresh_token: str = Body(..., embed=True, alias="refresh_token"),
        db: Session = Depends(get_db)
):
    """
    刷新 access token
    - 验证 refresh_token
    - 返回新的 access_token
    - 可选：实现 refresh token rotation
    """
    # 查找 refresh token
    db_token = db.query(RefreshToken).filter(
        RefreshToken.token == refresh_token,
        RefreshToken.is_revoked == False
    ).first()

    # 验证 token 是否存在且有效
    if not db_token:
        return get_response(INVALID_TOKEN,data=None)

    # 检查是否过期
    if db_token.expires_at < datetime.utcnow():
        # 标记为已撤销
        db_token.is_revoked = True
        db.commit()
        return get_response(INVALID_TOKEN, data=None)

    # 获取用户信息
    user = db.query(User).filter(User.id == db_token.user_id).first()
    if not user or not user.is_active:
        return get_response(USER_Banned,data=None)

    # 更新 last_used_at
    db_token.last_used_at = datetime.utcnow()
    db.commit()

    # 创建新的 access token
    access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
    access_token = create_access_token(
        subject=str(user.id),
        expires_delta=access_token_expires
    )

    # 可选：实现 refresh token rotation
    # 每次刷新时生成新的 refresh token，提高安全性
    if settings.REFRESH_TOKEN_ROTATION_ENABLED:
        # 撤销旧 token
        db_token.is_revoked = True
        db.commit()

        # 创建新的 refresh token
        new_refresh_token = create_refresh_token(str(user.id), db)

        return get_response(SUCCESS, data={
            "access_token": access_token,
            "refresh_token": new_refresh_token,
            "token_type": "bearer",
            "expires_in": settings.ACCESS_TOKEN_EXPIRE_MINUTES * 60
        })
    else:
        return get_response(SUCCESS, data={
            "access_token": access_token,
            "token_type": "bearer",
            "expires_in": settings.ACCESS_TOKEN_EXPIRE_MINUTES * 60
        })


@router.get("/me", response_model=Dict[str, Any])
async def get_current_user_info(
        current_user: User = Depends(get_current_user)
):
    """获取当前用户信息"""
    return get_response(SUCCESS, data={
        "id": str(current_user.id),
        "username": current_user.username,
        "email": current_user.email,
        "role": current_user.role.value,
        "is_active": current_user.is_active,
        "created_at": current_user.created_at.isoformat() if current_user.created_at else None
    })


@router.post("/logout", response_model=Dict[str, Any])
async def logout(
        refresh_token: Optional[str] = Body(None, embed=True, alias="refresh_token"),
        current_user: User = Depends(get_optional_current_user),
        db: Session = Depends(get_db)
):
    """
    用户登出
    - 撤销当前的 refresh token
    - 如果提供了用户信息，撤销该用户的所有 tokens
    """
    if refresh_token:
        # 撤销特定的 refresh token
        db_token = db.query(RefreshToken).filter(
            RefreshToken.token == refresh_token,
            RefreshToken.is_revoked == False
        ).first()

        if db_token:
            db_token.is_revoked = True
            db.commit()

    # 如果有当前用户信息，撤销该用户的所有 tokens
    if current_user:
        revoke_user_tokens(str(current_user.id), db)

    return get_response(SUCCESS)


@router.post("/revoke-all", response_model=Dict[str, Any])
async def revoke_all_tokens(
        current_user: User = Depends(get_current_user),
        db: Session = Depends(get_db)
):
    """
    撤销当前用户的所有 refresh tokens
    用于安全场景，如修改密码后
    """
    revoke_user_tokens(str(current_user.id), db)
    return get_response(SUCCESS)


# 清理过期的 refresh tokens（可以作为定时任务运行）
@router.delete("/cleanup-tokens", response_model=Dict[str, Any])
async def cleanup_expired_tokens(
        current_user: User = Depends(get_current_user),
        db: Session = Depends(get_db)
):
    """清理过期的 refresh tokens（需要管理员权限）"""
    if current_user.role.value != "admin":
        raise HTTPException(status_code=403, detail="需要管理员权限")

    # 删除过期的 tokens
    expired_count = db.query(RefreshToken).filter(
        RefreshToken.expires_at < datetime.utcnow()
    ).delete()

    # 删除已撤销超过30天的 tokens
    revoked_count = db.query(RefreshToken).filter(
        and_(
            RefreshToken.is_revoked == True,
            RefreshToken.created_at < datetime.utcnow() - timedelta(days=30)
        )
    ).delete()

    db.commit()

    return get_response(SUCCESS, data={
        "expired_deleted": expired_count,
        "revoked_deleted": revoked_count
    })